package cn.zzdt4j.config.springboot.starter.refresher.event;

import cn.hutool.core.util.ObjectUtil;
import cn.zzdt4j.adapter.web.WebThreadPoolHandlerChoose;
import cn.zzdt4j.adapter.web.WebThreadPoolService;
import cn.zzdt4j.common.api.NotifyRequest;
import cn.zzdt4j.common.api.ThreadPoolConfigChange;
import cn.zzdt4j.common.model.ThreadPoolParameter;
import cn.zzdt4j.common.model.ThreadPoolParameterInfo;
import cn.zzdt4j.core.config.ApplicationContextHolder;
import cn.zzdt4j.threadpool.dynamic.model.config.properties.BootstrapConfigProperties;
import cn.zzdt4j.threadpool.dynamic.model.config.properties.WebExecutorProperties;
import cn.zzdt4j.threadpool.message.core.request.WebChangeParameterNotifyRequest;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.springframework.core.annotation.Order;

import static cn.zzdt4j.config.springboot.starter.refresher.event.ThreadPoolConfigDynamicRefreshEventOrder.WEB_EXECUTOR_LISTENER;

/**
 * Web executor refresh listener.
 *
 * @author by <a href="mailto:ligang941012@gmail.com">gang.Li</a>
 * @since 2023/10/26 17:10
 */
@Slf4j
@Order(WEB_EXECUTOR_LISTENER)
@SuppressWarnings("rawtypes")
public class WebExecutorRefreshListener extends AbstractRefreshListener<WebExecutorProperties> {

    private final ThreadPoolConfigChange threadPoolConfigChange;

    public WebExecutorRefreshListener(ThreadPoolConfigChange threadPoolConfigChange) {
        this.threadPoolConfigChange = threadPoolConfigChange;
    }

    @Override
    @SuppressWarnings("unchecked")
    public void onApplicationEvent(@NonNull ThreadPoolConfigDynamicRefreshEvent event) {
        BootstrapConfigProperties bindableCoreProperties = event.getBootstrapConfigProperties();
        if (bindableCoreProperties.getWeb() == null) {
            return;
        }
        try {
            ThreadPoolParameterInfo nowParameterInfo = buildWebPoolParameter(bindableCoreProperties);
            if (nowParameterInfo != null) {
                WebThreadPoolHandlerChoose webThreadPoolHandlerChoose = ApplicationContextHolder.getBean(WebThreadPoolHandlerChoose.class);
                WebThreadPoolService choose = webThreadPoolHandlerChoose.choose();
                ThreadPoolParameterInfo beforePoolParameter = (ThreadPoolParameterInfo) choose.getWebThreadPoolParameter();

                // change
                if (nowParameterInfo.getCorePoolSize() == null) {
                    nowParameterInfo.setCorePoolSize(beforePoolParameter.getCorePoolSize());
                }
                if (nowParameterInfo.getMaximumPoolSize() == null) {
                    nowParameterInfo.setMaximumPoolSize(beforePoolParameter.getMaximumPoolSize());
                }
                if (nowParameterInfo.getKeepAliveTime() == null) {
                    nowParameterInfo.setKeepAliveTime(beforePoolParameter.getKeepAliveTime());
                }
                if (ObjectUtil.notEqual(nowParameterInfo.getCorePoolSize(), beforePoolParameter.getCorePoolSize())
                        || ObjectUtil.notEqual(nowParameterInfo.getMaximumPoolSize(), beforePoolParameter.getMaximumPoolSize())
                        || ObjectUtil.notEqual(nowParameterInfo.getKeepAliveTime(), beforePoolParameter.getKeepAliveTime())) {
                    choose.updateWebThreadPool(nowParameterInfo);
                    threadPoolConfigChange.sendPoolConfigChange(buildChangeRequest(beforePoolParameter, nowParameterInfo, choose));
                }

            }
        } catch (Exception ex) {
            log.error("Failed to modify web thread pool.", ex);
        }
    }

    /**
     * Constructing a request for web thread pool parameter change notification
     *
     * @param beforePoolParameter
     * @param nowParameterInfo
     * @param webThreadPoolService
     * @return
     */
    private WebChangeParameterNotifyRequest buildChangeRequest(ThreadPoolParameterInfo beforePoolParameter, ThreadPoolParameterInfo nowParameterInfo,
                                                               WebThreadPoolService webThreadPoolService) {
        WebChangeParameterNotifyRequest changeNotifyRequest = WebChangeParameterNotifyRequest.builder()
                .beforeCorePoolSize(beforePoolParameter.getCorePoolSize())
                .nowCorePoolSize(nowParameterInfo.getCorePoolSize())
                .beforeMaximumPoolSize(beforePoolParameter.getMaximumPoolSize())
                .nowMaximumPoolSize(nowParameterInfo.getMaximumPoolSize())
                .beforeKeepAliveTime(beforePoolParameter.getKeepAliveTime())
                .nowKeepAliveTime(nowParameterInfo.getKeepAliveTime()).build();
        changeNotifyRequest.setThreadPoolId(webThreadPoolService.getWebContainerType().getName());
        return changeNotifyRequest;
    }

    private ThreadPoolParameterInfo buildWebPoolParameter(BootstrapConfigProperties bindableCoreProperties) {
        ThreadPoolParameterInfo threadPoolParameterInfo = null;
        WebExecutorProperties bindableCorePropertiesWeb = bindableCoreProperties.getWeb();
        if (bindableCoreProperties.getWeb() != null && bindableCorePropertiesWeb.getEnable() && match(bindableCorePropertiesWeb)) {
            threadPoolParameterInfo = ThreadPoolParameterInfo.builder()
                    .corePoolSize(bindableCorePropertiesWeb.getCorePoolSize())
                    .maximumPoolSize(bindableCorePropertiesWeb.getMaximumPoolSize())
                    .keepAliveTime(bindableCorePropertiesWeb.getKeepAliveTime())
                    .build();
        }
        return threadPoolParameterInfo;
    }
}
